package org.dant.ant.extension.xml;

import java.io.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.util.DOMElementWriter;
import org.dant.ant.extension.tasks.internal.Endpoint;
import org.dant.ant.extension.tasks.internal.RemoteResponse;

public class XMLResultFormatter implements XMLConstants {

    private Document doc;
    private Element rootElement;
  
    private static DocumentBuilder getDocumentBuilder() {
        try {
            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (Exception exc) {
            throw new ExceptionInInitializerError(exc);
        }
    }
  
    public synchronized void writeResults(RemoteResponse res, File basedir) throws BuildException {
        doc = getDocumentBuilder().newDocument();
        switch (res.getTagType()) {
          case RemoteResponse.TASK:
              rootElement = doc.createElement(TASK);
              break;
      
          case RemoteResponse.TESTCASE:
              rootElement = doc.createElement(TESTCASE);
              break;
      
          case RemoteResponse.TESTSUITE:
              rootElement = doc.createElement(TESTSUITE);
              break;
      
          default:
              System.err.println("Invalid result type detected: " + res);
              return;
        }
        
        rootElement.setAttribute(ATTR_RESULT, printResult(res));
        rootElement.setAttribute(ATTR_NAME, res.getName());
        rootElement.setAttribute(ATTR_PACKAGE, res.getPackageName());

        if (res.getTagType() == RemoteResponse.TASK) {
            if (res.getMultiSited()) {
                Endpoint ep = new Endpoint(res.getFullURL());
                rootElement.setAttribute(ATTR_TASK_NAME, res.getTaskName() + "." + ep.getHostname());
            } else {
                rootElement.setAttribute(ATTR_TASK_NAME, res.getTaskName());
            }
            rootElement.setAttribute(ATTR_TIME, "" + res.getTimes() / 1000);
            rootElement.setAttribute(ATTR_HOST, "" + res.getFullURL());
            rootElement.setAttribute(ATTR_DUPLICATE, "" + res.getDuplicate());
            addCommand(rootElement, res);
            addOutput(rootElement, res);
        }
        addDescription(rootElement, res);
    
        String filename = genFileName(res);
        File outfile = new File(basedir, filename);
        OutputStream out = null;
    
        try {
            out = new FileOutputStream(outfile);
        } catch (IOException e) {
            throw new BuildException("Unable to write results file " + outfile.getName() + ": " + outfile);
        }
    
        Writer wri = null;

        try {
            wri = new OutputStreamWriter(out, "UTF8");
            wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
            (new DOMElementWriter()).write(rootElement, wri, 0, "  ");
            wri.flush();
        } catch (IOException exc) {
            throw new BuildException("Unable to write log file", exc);
        } finally {
            if (out != System.out && out != System.err) {
                if (wri != null) {
                    try {
                        wri.close();
                    } catch (IOException e) {
                        //ignore
                    }
                }
            }
        }
    }
  
    private void addDescription(Element componentElement, RemoteResponse res) {
        Element descrElement = doc.createElement(DESCRIPTION);
    
        componentElement.appendChild(descrElement);
        addText(descrElement, res.getResult());
    }
  
    private void addCommand(Element componentElement, RemoteResponse res) {
        Element execElement = doc.createElement(COMMAND);
    
        componentElement.appendChild(execElement);
        addText(execElement, res.getCommand());
    }
  
    private void addOutput(Element componentElement, RemoteResponse res) {
        Element outElement = doc.createElement(SYSTEM_OUT);
    
        componentElement.appendChild(outElement);
        addText(outElement, res.getStdOutBuffer().toString());
        Element errElement = doc.createElement(SYSTEM_ERR);
    
        componentElement.appendChild(errElement);
        addText(errElement, res.getStdErrBuffer().toString());
    }
  
    private void addText(Element elem, String text) {
        Text textNode = doc.createTextNode(text);
        elem.appendChild(textNode);
    }
  
    private String printResult(RemoteResponse res) {
        if (res.isSuccess()) {
            return "PASSED";
        } else {
            return "FAILED";
        }
    }
  
    private String printType(RemoteResponse res) {
        int type = res.getTagType();
    
        switch (type) {
        case RemoteResponse.TASK:
            return "TASK";
              
        case RemoteResponse.TESTSUITE:
            return "SUITE";
              
        case RemoteResponse.TESTCASE:
            return "TESTCASE";
              
        default:
            return "UNKN";
        }
    }
  
    private String genFileName(RemoteResponse res) {
        StringBuffer ret = new StringBuffer();
    
        ret.append(printType(res));
        String pkg = res.getPackageName();
    
        if (pkg != null && !"".equals(pkg)) {
            ret.append("_");
            ret.append(formatName(pkg));
        }
        String name = formatName(res.getName());
    
        if (name != null && !"".equals(name)) {
            ret.append("_");
            ret.append(res.getName());
        }

        String taskName = res.getTaskName();
        if (taskName != null && !"".equals(taskName)) {
            ret.append("_");
            ret.append(taskName);
            if (res.getMultiSited()) {
                Endpoint ep = new Endpoint(res.getFullURL());
                ret.append("_");
                ret.append(ep.getHostname());
            }
        }
        ret.append(".xml");
        return ret.toString();
    }

    private String formatName(String name) {
        name = name.replace(' ', '.');
        name = name.replace('/', '_');
        name = name.replace('\\', '_');
        return name;
    }
}
